/***********************************************************
*	game_states.h
*
*	This file contains the game states the game will go
*	through.
*	Implement each state of a game state here: Begin,
*	Update, and End.  Do not directly set the 
*	g_gameState varriable.  it is only to be read.
*	To change the game state, use SetGameState().
*	This function performs all the transition functions
*	for the current and next state.
***********************************************************/

#ifndef GAME_STATES_H
#define GAME_STATES_H

#include "globals.h" // drawing functions
#include "input.h"
#include "drawing.h"
#include "sprite_handler.h"
#include "scrolling.h"
#include "timer.h"
#include "characters/characters.h"
#include "sound.h"

#include "data/splash_bmp.h"
#include "data/title_bmp.h"
#include "data/victory_bmp.h"
#include "data/defeat_bmp.h"

#include "collision.h"
#include "world_camera.h"

#include "velocity.h"

////////////////////////////////////////////////////////////
// Game States
// Indiciate how the Game is to be updated
// Splash Screen Levels
// Title/Control Screen Levels
// Game Levels
// End Screen Level
////////////////////////////////////////////////////////////
typedef enum
{
    SPLASH_SCREEN = 0,
    TITLE_SCREEN,
    GAMEMODE,
    VICTORY_SCREEN,
	DEFEAT_SCREEN
} GameState;

////////////////////////////////////////////////////////////
// Game State Prototypes
// These functions will be called when switching to, running
// in, and exiting from each game state.
// Begin: Will be called when the state is entered
// Update: Will be called every frame while this state is
//  the active state
// End: Will be called when the active state is switched
//  or the state ends.
// SetGameState: switches to the given state
////////////////////////////////////////////////////////////
void BeginSplashScreen();
void UpdateSplashScreen();
void EndSplashScreen();

void BeginTitleScreen();
void UpdateTitleScreen();
void EndTitleScreen();

void BeginGameMode();
void UpdateGameMode();
void EndGameMode();

void BeginVictoryScreen();
void UpdateVictoryScreen();
void EndVictoryScreen();

void BeginDefeatScreen();
void UpdateDefeatScreen();
void EndDefeatScreen();

void SetGameState( GameState const next_state );
void SetDeathState();
void SetVictoryState();

////////////////////////////////////////////////////////////
// Global!!!!
////////////////////////////////////////////////////////////

// Global Game State, should only be observed
// use SetGameState to change state
GameState g_gameState = -1;



////////////////////////////////////////////////////////////
// Game State Functions
////////////////////////////////////////////////////////////
#define SPLASH_SCREEN_TIME 5000

int splash_screen_time = 0;

////////////////////////////////////////////////////////////
// Function: BeginSplashScreen
// Sets up the first screen
////////////////////////////////////////////////////////////
void BeginSplashScreen()
{
	SetMode(BG2_ENABLE | MODE_3);
	DrawImage3(splash_Bitmap, 0,0,240,160);
	splash_screen_time = 0;
}


////////////////////////////////////////////////////////////
// Function: UpdateSplashScreen
// updates splash screen. Waits for player input
////////////////////////////////////////////////////////////
void UpdateSplashScreen()
{
	splash_screen_time += getElapsedTime();
	if ( splash_screen_time >= SPLASH_SCREEN_TIME )
	{
		SetGameState(TITLE_SCREEN);
	}
}


////////////////////////////////////////////////////////////
// Function: EndSplashScreen
// records time it took for player input
////////////////////////////////////////////////////////////
void EndSplashScreen()
{
	splash_screen_time = 0;
}


////////////////////////////////////////////////////////////
// Function: BeginTitleScreen
// Sets up title screen resources
////////////////////////////////////////////////////////////
void BeginTitleScreen()
{
	SetMode(BG2_ENABLE | MODE_3);
	DrawImage3(title_Bitmap, 0,0,240,160);
}


////////////////////////////////////////////////////////////
// Function: UpdateTitleScreen
// updates title screen. Waits for player input
////////////////////////////////////////////////////////////
void UpdateTitleScreen()
{

	if(!SampleLength)
		PlaySound(&s_menu);
	if(buttonHit(BUTTON_A))
		SetGameState(GAMEMODE);
}


////////////////////////////////////////////////////////////
// Function: EndTitleScreen
// Ends title screen
////////////////////////////////////////////////////////////
void EndTitleScreen()
{
	PlaySound(&s_turret);
}


#define TOTAL_ENEMIES 			25
#define STARTING_NUM_ENEMIES 	6

#define SPAWN_ONE_X 512
#define SPAWN_ONE_Y 512
#define SPAWN_TWO_X 1536
#define SPAWN_TWO_Y 512
#define SPAWN_THREE_X 512
#define SPAWN_THREE_Y 1536
#define SPAWN_FOUR_X 1536
#define SPAWN_FOUR_Y 1536
#define SPAWN_FIVE_X 1024
#define SPAWN_FIVE_Y 1024
#define SPAWN_SIX_X 1024
#define SPAWN_SIX_Y 512
#define SPAWN_SEVEN_X 1024
#define SPAWN_SEVEN_Y 1536

#define ENEMY_SPEED_ONE 15
#define ENEMY_SPEED_TWO 8
#define ENEMY_SPEED_THREE 7
#define ENEMY_SPEED_FOUR 6

// sprite handler index we should never touch
#define MAX_INDEX	8

int g_nextEnemyNumber = 0;		// next number of the next enemy to head out ( who's on deck )

int g_enemiesKilled = 0;

// Add Enemy to the game, based off the enemy number this is
// This is so we can specify where exaactly we want each enemy to be
void addEnemy( int enemy_number )
{
	// based off the index, place the new enemy at a specifc point
	switch( enemy_number )
	{
		case 0: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, 280, 85, ENEMY_SPEED_ONE, 1); break;
		case 1: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_TWO_X, SPAWN_TWO_Y, ENEMY_SPEED_ONE, 1); break;
		case 2: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_THREE_X, SPAWN_THREE_Y, ENEMY_SPEED_ONE, 1); break;
		case 3: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_FOUR_X, SPAWN_FOUR_Y, ENEMY_SPEED_ONE, 1); break;
		case 4: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_FIVE_X, SPAWN_FIVE_Y, ENEMY_SPEED_ONE, 1); break;
		case 5: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_SIX_X, SPAWN_SIX_Y, ENEMY_SPEED_ONE, 1); break;
		case 6: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_SEVEN_X, SPAWN_SEVEN_Y, ENEMY_SPEED_ONE, 1); break;
		
		case 7: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_THREE_X, SPAWN_THREE_Y, ENEMY_SPEED_TWO, 2); break;
		case 8: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_TWO_X, SPAWN_TWO_Y, ENEMY_SPEED_ONE, 1); break;
		case 9: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_FOUR_X, SPAWN_FOUR_Y, ENEMY_SPEED_TWO, 2); break;
		case 10: NewEnemy(FRAME_ENEMY_8x8_1, SD_8x8, SPAWN_THREE_X, SPAWN_THREE_Y, ENEMY_SPEED_ONE, 1); break;
		case 11: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_FIVE_X, SPAWN_FIVE_Y, ENEMY_SPEED_TWO, 2); break;
		case 12: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_SIX_X, SPAWN_SIX_Y, ENEMY_SPEED_TWO, 2); break;
		case 13: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_SEVEN_X, SPAWN_SEVEN_Y, ENEMY_SPEED_TWO, 2); break;
		
		case 14: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_FIVE_X, SPAWN_FIVE_Y, ENEMY_SPEED_THREE, 3); break;
		case 15: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_ONE_X, SPAWN_ONE_Y, ENEMY_SPEED_TWO, 2); break;
		case 16: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_TWO_X, SPAWN_TWO_Y, ENEMY_SPEED_TWO, 2); break;
		case 17: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_THREE_X, SPAWN_THREE_Y, ENEMY_SPEED_TWO, 2); break;
		case 18: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_SIX_X, SPAWN_SIX_Y, ENEMY_SPEED_THREE, 3); break;
		case 19: NewEnemy(FRAME_ENEMY_16x16_PURPLE_1, SD_16x16, SPAWN_FOUR_X, SPAWN_FOUR_Y, ENEMY_SPEED_TWO, 2); break;	
		case 20: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_SEVEN_X, SPAWN_SEVEN_Y, ENEMY_SPEED_THREE, 3); break;
		
		case 21: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_ONE_X, SPAWN_ONE_Y, ENEMY_SPEED_THREE, 3); break;
		case 22: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_TWO_X, SPAWN_TWO_Y, ENEMY_SPEED_THREE, 3); break;
		case 23: NewEnemy(FRAME_ENEMY_16x16_BLUE_1, SD_16x16, SPAWN_THREE_X, SPAWN_THREE_Y, ENEMY_SPEED_THREE, 3); break;
		case 24: NewEnemy(FRAME_BOSS_1, SD_32x32, SPAWN_FOUR_X, SPAWN_FOUR_Y, ENEMY_SPEED_FOUR, 4); break;
	}
}

void addNextEnemy()
{
	// unsigned short* pcav = 0x3000000;
	
	// First double check and make sure we are not going into the danger zone
	if ( getNextSpriteHandlerID() >= MAX_INDEX )
	{
		// NOPE, gtfo, we will crash this stupid fucking game
		return;
	}
	
	// Also make sure we are not done with enemies
	if ( g_nextEnemyNumber == TOTAL_ENEMIES )
	{
		return;
	}
	
	
	// *pcav = g_nextEnemyNumber;
	
	// If not max index, then add the next enemy
	addEnemy( g_nextEnemyNumber );
	++g_nextEnemyNumber;
}

// onenemy death add next enemey

void replaceWithExplosion( SpriteHandler * sprite )
{
	sprite->alive = FALSE;	// kill the sprite
	
	// get world position data
	int x = -1;
	int y = -1;
	
	if ( ! getMapCoordinates( sprite, &x, &y ) )
	{
		return;
	}
	
	// make new explosition sprite handler
	newExplosion( x, y ); // give it the world positions of the old sprite
	
	// this should immedietly use the sprite handler index of the old sprite for the next explosion
	PlaySound( &s_asplode );
}

////////////////////////////////////////////////////////////
// Function: BeginGameMode
// Sets up the Main game level.  Set up resources, map, etc.
////////////////////////////////////////////////////////////
void BeginGameMode()
{
	// set video mode 0 with backgrounds 0 & 1 & inform GBA that we're using sprites
	SetMode(0 | BG0_ENABLE | BG1_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);
    	
	initSpriteHandlerSystem();			// Initialize SpriteHandler System
    InitializeScrollingBackground();    // found in scrolling.h
	
	LoadContentForSprites(); // found in sprite_handler.h
    LoadContentForScrollingBackground();    // found in scrolling.h
    
    VelocityInit();
	
	// Here we place 5 characters into the world.
	// Once we place the sixth character, the game crashes
	playerInit();				// WHAT IS GOING ON!
	
	//newExplosion( 100, 100 );
	
	//return;
	
	for ( g_nextEnemyNumber = 0 ; 
		  g_nextEnemyNumber < STARTING_NUM_ENEMIES ;  )
	{
		addNextEnemy();
	}
}


////////////////////////////////////////////////////////////
// Function: UpdateGameMode
// updates main game mode.  Very complex.
////////////////////////////////////////////////////////////
void UpdateGameMode()
{
	updateSpriteHandlers();
	cullForInvisible();
	handleCollisions();
    UpdateScrollingBackground();  // found in scrolling.h; currently scrolls background w/ directional buttons
}


////////////////////////////////////////////////////////////
// Function: EndGameMode
// Release all game resources for game, etc
////////////////////////////////////////////////////////////
void EndGameMode()
{
	cleanAllSprites();
}


////////////////////////////////////////////////////////////
// Function: BeginVictoryScreen
// Sets up victory screen
////////////////////////////////////////////////////////////
void BeginVictoryScreen()
{
	SetMode(BG2_ENABLE | MODE_3);
	DrawImage3(victory_Bitmap, 0,0,240,160);
	PlaySound(&s_win);
}


////////////////////////////////////////////////////////////
// Function: UpdateVictoryScreen
// updates victory screen
////////////////////////////////////////////////////////////
void UpdateVictoryScreen()
{	
	if(buttonHit(BUTTON_A))
		SetGameState(TITLE_SCREEN);
}


////////////////////////////////////////////////////////////
// Function: EndVictoryScreen
// Releases resources used in victory screen
////////////////////////////////////////////////////////////
void EndVictoryScreen()
{
	PlaySound(&s_turret);
}

////////////////////////////////////////////////////////////
// Function: BeginDefeatScreen
// Sets up victory screen
////////////////////////////////////////////////////////////
void BeginDefeatScreen()
{
	SetMode(BG2_ENABLE | MODE_3);
	DrawImage3(defeat_Bitmap, 0,0,240,160);
	PlaySound(&s_loss);
}


////////////////////////////////////////////////////////////
// Function: UpdateDefeatScreen
// updates victory screen
////////////////////////////////////////////////////////////
void UpdateDefeatScreen()
{	
	if(buttonHit(BUTTON_A))
		SetGameState(TITLE_SCREEN);
}


////////////////////////////////////////////////////////////
// Function: EndDefeatScreen
// Releases resources used in victory screen
////////////////////////////////////////////////////////////
void EndDefeatScreen()
{
	PlaySound(&s_turret);
}


////////////////////////////////////////////////////////////
// Function: SetGameState
// Sets the game state.  If currently in a gamestate, calles
// state's end function.  Calls new states' begin function.
////////////////////////////////////////////////////////////
void SetGameState( GameState const next_state )
{
    // what is the current GameState?
    switch ( g_gameState )
    {
        case SPLASH_SCREEN:
        {
            EndSplashScreen();
            break;
        }
        case TITLE_SCREEN:
        {
            EndTitleScreen();
            break;
        }
        case GAMEMODE:
        {
            EndGameMode();
            break;
        }
        case VICTORY_SCREEN:
        {
            EndVictoryScreen();
            break;
        }
		case DEFEAT_SCREEN:
        {
            EndDefeatScreen();
            break;
        }
        default:
            break;
    }
    
    // Assigne new state
    g_gameState = next_state;
    
    // How do we start the new gamestate?
    switch ( g_gameState )
    {
        case SPLASH_SCREEN:
        {
            BeginSplashScreen();
            break;
        }
        case TITLE_SCREEN:
        {
            BeginTitleScreen();
            break;
        }
        case GAMEMODE:
        {
            BeginGameMode();
            break;
        }
        case VICTORY_SCREEN:
        {
            BeginVictoryScreen();
            break;
        }
		case DEFEAT_SCREEN:
        {
            BeginDefeatScreen();
            break;
        }
        default:
            break;
    }
}

void SetDeathState()
{
	SetGameState( DEFEAT_SCREEN );
}

void SetVictoryState()
{
	SetGameState( VICTORY_SCREEN );
}

#endif

